package com.derbysoft.synxisadapter.dswitch2.translator.impl;

import com.derbysoft.core.utils.DateTimeUtils;
import com.derbysoft.remote.dswitch.core.GenericRQ;
import com.derbysoft.remote.dswitch.generic.constants.CardCode;
import com.derbysoft.remote.dswitch.generic.constants.TransactionActionType;
import com.derbysoft.remote.dswitch.generic.constants.UniqueIDType;
import com.derbysoft.remote.dswitch.generic.dto.*;
import com.derbysoft.remote.dswitch.hotel.dto.*;
import com.derbysoft.synxisadapter.commons.utils.Constant;
import com.derbysoft.synxisadapter.commons.dto.synxis.*;
import com.derbysoft.synxisadapter.commons.exception.HotelNotFoundException;
import com.derbysoft.synxisadapter.commons.exception.InvalidFieldException;
import com.derbysoft.synxisadapter.commons.model.Channel;
import com.derbysoft.synxisadapter.commons.model.Hotel;
import com.derbysoft.synxisadapter.dswitch2.translator.Translator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.List;

public class HotelReservationRQTranslatorImpl extends AbstractRQTranslator
        implements Translator<GenericRQ<HotelReservationRQ>, List<OTAHotelResRQ>, Void> {

    @Override
    public List<OTAHotelResRQ> translate(GenericRQ<HotelReservationRQ> from, Void param) {
        ArrayList<OTAHotelResRQ> otaHotelResRQs = new ArrayList<OTAHotelResRQ>();
        ReservationRoomStayDTO roomStay = from.getPayload().getHotelReservation().getRoomStays().get(0);
        OTAHotelResRQ otaHotelResRQ = translateOTAHotelResAvailRQ(from, roomStay, from.getPayload().getHotelReservationIDs());
        for (int i = 0; i < roomStay.getNumberOfUnits(); i++) {
            otaHotelResRQs.add(otaHotelResRQ);
        }
        return otaHotelResRQs;
    }

    private OTAHotelResRQ translateOTAHotelResAvailRQ(GenericRQ<HotelReservationRQ> from,
                                                      ReservationRoomStayDTO reservationRoomStayDTO,
                                                      List<HotelReservationIDDTO> hotelReservationIDs) {
        OTAHotelResRQ otaHotelAvailRQ = createOTAHotelResRQ();
        otaHotelAvailRQ.setPrimaryLangID(Constant.PRIMARY_LANG_ID);
        otaHotelAvailRQ.setPOS(translatePOS(from.getHeader().getSource()));
        otaHotelAvailRQ.setResStatus(translateResStatus(from.getPayload().getResActionType()));

        Channel channel = findChannel(from.getHeader().getSource().getUniqueID());
        otaHotelAvailRQ.setHotelReservations(
                translateHotelReservations(
                        channel,
                        reservationRoomStayDTO,
                        from.getPayload().getHotelReservation().getHotelRef(),
                        hotelReservationIDs));

        return otaHotelAvailRQ;
    }

    private String translateResStatus(TransactionActionType resActionType) {
        switch (resActionType) {
            case BOOK:
                return TransactionActionType.COMMIT.getValue();
            case INITIATE:
                return TransactionActionType.INITIATE.getValue();
            case IGNORE:
                return TransactionActionType.IGNORE.getValue();
        }

        throw new InvalidFieldException("ResActionType", resActionType.getValue());
    }

    private ArrayOfHotelReservation translateHotelReservations(Channel channel,
                                                               ReservationRoomStayDTO reservationRoomStayDTO,
                                                               HotelRefDTO hotelRef,
                                                               List<HotelReservationIDDTO> hotelReservationIDs) {
        ArrayOfHotelReservation arrayOfHotelReservation = createArrayOfHotelReservation();
        arrayOfHotelReservation.getHotelReservation().add(
                translateHotelReservation(channel, reservationRoomStayDTO, hotelRef, hotelReservationIDs));
        return arrayOfHotelReservation;
    }

    private HotelReservation translateHotelReservation(Channel channel,
                                                       ReservationRoomStayDTO reservationRoomStayDTO,
                                                       HotelRefDTO hotelRef,
                                                       List<HotelReservationIDDTO> hotelReservationIDs) {
        HotelReservation hotelReservation = createHotelReservation();
        hotelReservation.setRoomStays(translateRoomStays(reservationRoomStayDTO, hotelRef));
        hotelReservation.setResGuests(
                translateResGuests(
                        reservationRoomStayDTO.getContactPersons().get(0),
                        reservationRoomStayDTO.getGuests()));
        hotelReservation.setResGlobalInfo(
                translateResGlobalInfo(channel,
                        reservationRoomStayDTO,
                        hotelReservationIDs));
        return hotelReservation;
    }

    private ResGlobalInfo translateResGlobalInfo(Channel channel,
                                                 ReservationRoomStayDTO reservationRoomStayDTO,
                                                 List<HotelReservationIDDTO> hotelReservationIDs) {
        ResGlobalInfo resGlobalInfo = createResGlobalInfo();
        translateIataNumber(channel, resGlobalInfo);
        if (CollectionUtils.isNotEmpty(reservationRoomStayDTO.getGuarantees())) {
            resGlobalInfo.setGuarantee(translateGuarantee(reservationRoomStayDTO.getGuarantees()));
        }
        resGlobalInfo.setHotelReservationIDs(translateHotelReservationIDs(channel, hotelReservationIDs));
        resGlobalInfo.setComments(translateComments(reservationRoomStayDTO));
        return resGlobalInfo;
    }

    private ArrayOfComment translateComments(ReservationRoomStayDTO reservationRoomStayDTO) {
        ArrayOfComment arrayOfComment = createArrayOfComment();
        if (CollectionUtils.isNotEmpty(reservationRoomStayDTO.getComments())) {
            for (String comment : reservationRoomStayDTO.getComments()) {
                if (StringUtils.isNotBlank(comment)) {
                    arrayOfComment.getComment().add(translateComment(comment));
                }
            }
        }
        if (CollectionUtils.isNotEmpty(reservationRoomStayDTO.getSpecialRequests())) {
            for (SpecialRequestDTO specialRequestDTO : reservationRoomStayDTO.getSpecialRequests()) {
                if (StringUtils.isNotBlank(specialRequestDTO.getText())) {
                    arrayOfComment.getComment().add(translateComment(specialRequestDTO.getText()));
                }
            }
        }
        return arrayOfComment;
    }

    private Comment translateComment(String text) {
        Comment comment = createComment();
        comment.setText(text);
        return comment;
    }

    private ArrayOfHotelReservationID translateHotelReservationIDs(Channel channel,
                                                                   List<HotelReservationIDDTO> hotelReservationIDs) {
        for (HotelReservationIDDTO hotelReservationIDDTO : hotelReservationIDs) {
            if (hotelReservationIDDTO.getResIDType() == UniqueIDType.TRAVEL_AGENCY) {
                ArrayOfHotelReservationID arrayOfHotelReservationID = new ArrayOfHotelReservationID();

                arrayOfHotelReservationID.getHotelReservationID().add(
                        translateHotelReservationID(
                                channel.getId(),
                                hotelReservationIDDTO));
                return arrayOfHotelReservationID;
            }
        }
        return null;
    }

    private HotelReservationID translateHotelReservationID(String channel,
                                                           HotelReservationIDDTO hotelReservationIDDTO) {
        HotelReservationID hotelReservationID = new HotelReservationID();
        hotelReservationID.setResIDType(Constant.HOTEL_RES_ID_TYPE);
        hotelReservationID.setResIDSource(channel);
        hotelReservationID.setResIDValue(hotelReservationIDDTO.getResIDValue());
        return hotelReservationID;
    }


    private Guarantee translateGuarantee(List<GuaranteeDTO> guaranteeDTOs) {
        Guarantee guarantee = createGuarantee();
        guarantee.setGuaranteesAccepted(createArrayOfGuaranteeAccepted());
        for (GuaranteeDTO guaranteeDTO : guaranteeDTOs) {
            guarantee.getGuaranteesAccepted().getGuaranteeAccepted().add(translateGrangeesAccepted(guaranteeDTO));
        }
        return guarantee;
    }

    private GuaranteeAccepted translateGrangeesAccepted(GuaranteeDTO guaranteeDTO) {
        GuaranteeAccepted guaranteeAccepted = createGuaranteeAccepted();
        switch (guaranteeDTO.getGuaranteeType()) {
            case CreditCard:
                guaranteeAccepted.setPaymentCard(translatePaymentCard(guaranteeDTO.getPaymentCard()));
                break;
            case Prepay:
                break;
        }
        return guaranteeAccepted;
    }

    private PaymentCard translatePaymentCard(PaymentCardDTO paymentCardDTO) {
        PaymentCard paymentCard = createPaymentCard();
        if (paymentCardDTO.getCardCode() == CardCode.AMERICAN_EXPRESS) {
            paymentCard.setCardCode("AX");
        } else {
            paymentCard.setCardCode(paymentCardDTO.getCardCode().getCode());
        }
        paymentCard.setCardHolderName(paymentCardDTO.getCardHolderName());
        paymentCard.setExpireDate(paymentCardDTO.getExpireDate());
        paymentCard.setCardNumber(paymentCardDTO.getCardNumber());
        paymentCard.setSeriesCode(paymentCardDTO.getSeriesCode());
        return paymentCard;
    }


    private void translateIataNumber(Channel channel, ResGlobalInfo resGlobalInfo) {
        if (StringUtils.isNotBlank(channel.getIataNumber())) {
            resGlobalInfo.setProfiles(createArrayOfProfileInfo());
            ProfileInfo profileInfo = new ProfileInfo();
            resGlobalInfo.getProfiles().getProfileInfo().add(profileInfo);

            profileInfo.getUniqueID().add(
                    translateUniqueID(UniqueIDType.TRAVEL_AGENCY.getCode(),
                            channel.getIataNumber(), Constant.CONTEXT_IATA));
        }
    }

    private ArrayOfResGuest translateResGuests(CustomerDTO contact, List<CustomerDTO> guests) {
        ArrayOfResGuest arrayOfResGuest = createArrayOfResGuest();
        for (CustomerDTO guest : guests) {
            arrayOfResGuest.getResGuest().add(translateResGuest(contact, guest));
        }
        return arrayOfResGuest;
    }

    private ResGuest translateResGuest(CustomerDTO contact, CustomerDTO guest) {
        ResGuest resGuest = createResGuest();
        resGuest.setProfiles(translateProfiles(contact, guest));
        return resGuest;
    }

    private ArrayOfProfileInfo translateProfiles(CustomerDTO contact, CustomerDTO guest) {
        ArrayOfProfileInfo arrayOfProfileInfo = createArrayOfProfileInfo();
        arrayOfProfileInfo.getProfileInfo().add(translateProfileInfo(contact, guest));
        return arrayOfProfileInfo;
    }

    private ProfileInfo translateProfileInfo(CustomerDTO contact, CustomerDTO guest) {
        ProfileInfo profileInfo = createProfileInfo();
        profileInfo.setProfile(translateProfile(contact, guest));
        return profileInfo;
    }

    private Profile translateProfile(CustomerDTO contact, CustomerDTO guest) {
        Profile profile = createProfile();
        profile.setCustomer(translateCustomer(contact, guest));
        return profile;
    }

    private Customer translateCustomer(CustomerDTO contact, CustomerDTO guest) {
        Customer customer = createCustomer();
        customer.setPersonName(translatePersonName(guest.getPersonName()));
        customer.setContactPerson(translateContactPerson(contact.getPersonName()));
        customer.setAddress(translateAddress(contact.getAddresses()));
        if (CollectionUtils.isNotEmpty(contact.getEmails())) {
            customer.setEmail(contact.getEmails().get(0).getValue());
        }
        if (CollectionUtils.isNotEmpty(contact.getTelephones())) {
            customer.setTelephone(translateTelephoneInfo(contact.getTelephones().get(0)));
        }
        return customer;
    }

    private AddressInfo translateAddress(List<String> addresses) {
        if (CollectionUtils.isEmpty(addresses)) {
            return null;
        }

        AddressInfo addressInfo = createAddressInfo();
        addressInfo.getAddressLine().addAll(addresses);
        return addressInfo;
    }

    private ContactPerson translateContactPerson(PersonNameDTO personNameDTO) {
        ContactPerson contactPerson = createContactPerson();
        PersonName personName = new PersonName();
        personName.setGivenName(personNameDTO.getGivenName());
        personName.setSurname(personNameDTO.getSurname());
        contactPerson.setPersonName(personName);

        return contactPerson;
    }

    private TelephoneInfo translateTelephoneInfo(TelephoneDTO telephoneDTO) {
        TelephoneInfo telephoneInfo = createTelephoneInfo();
        telephoneInfo.setPhoneNumber(telephoneDTO.getNumber());
        return telephoneInfo;
    }

    private PersonName translatePersonName(PersonNameDTO personNameDTO) {
        PersonName personName = createPersonName();
        personName.setNameTitle(personNameDTO.getNameTitle());
        personName.setGivenName(personNameDTO.getGivenName());
        personName.setSurname(personNameDTO.getSurname());
        return personName;
    }

    private ArrayOfRoomStay translateRoomStays(ReservationRoomStayDTO reservationRoomStayDTO, HotelRefDTO hotelRef) {
        ArrayOfRoomStay arrayOfRoomStay = createArrayOfRoomStay();
        arrayOfRoomStay.getRoomStay().add(translateRoomStay(reservationRoomStayDTO, hotelRef));
        return arrayOfRoomStay;
    }

    private RoomStay translateRoomStay(ReservationRoomStayDTO reservationRoomStayDTO, HotelRefDTO hotelRef) {
        RoomStay roomStay = createRoomStay();
        roomStay.setRoomTypes(translateRoomTypes(reservationRoomStayDTO));
        roomStay.setRatePlans(translateRatePlans(reservationRoomStayDTO));
        roomStay.setGuestCounts(translateGuestCounts(reservationRoomStayDTO.getGuestCounts()));
        roomStay.setTimeSpan(translateTimeSpan(reservationRoomStayDTO.getStayDateRange()));
        roomStay.setBasicPropertyInfo(translateBasicPropertyInfo(hotelRef));
        return roomStay;
    }

    private HotelReferenceGroup translateBasicPropertyInfo(HotelRefDTO hotelRef) {
        HotelReferenceGroup hotelReferenceGroup = createHotelReferenceGroup();
        Hotel hotel = hotelRepository.findByPassport(hotelRef.getCode(), Hotel.Status.ENABLED);
        if (hotel == null) {
            throw new HotelNotFoundException(hotelRef.getCode());
        }
        hotelReferenceGroup.setHotelCode(hotel.getId());
        return hotelReferenceGroup;
    }

    private DateTimeSpanType translateTimeSpan(StayDateRangeDTO stayDateRangeDTO) {
        DateTimeSpanType dateTimeSpanType = createDateTimeSpanType();
        dateTimeSpanType.setStart(DateTimeUtils.formatDate(stayDateRangeDTO.getCheckInDate()));
        dateTimeSpanType.setEnd(DateTimeUtils.formatDate(stayDateRangeDTO.getCheckOutDate()));
        return dateTimeSpanType;
    }

    private ArrayOfRatePlan translateRatePlans(ReservationRoomStayDTO reservationRoomStayDTO) {
        ArrayOfRatePlan arrayOfRatePlan = createArrayOfRatePlan();
        arrayOfRatePlan.getRatePlan().add(translateRatePlan(reservationRoomStayDTO));
        return arrayOfRatePlan;
    }

    private RatePlan translateRatePlan(ReservationRoomStayDTO reservationRoomStayDTO) {
        RatePlan ratePlan = createRatePlan();
        ratePlan.setRatePlanCode(reservationRoomStayDTO.getRatePlanCode());
        return ratePlan;
    }

    private ArrayOfRoomType translateRoomTypes(ReservationRoomStayDTO reservationRoomStayDTO) {
        ArrayOfRoomType arrayOfRoomType = createArrayOfRoomType();
        arrayOfRoomType.getRoomType().add(translateRoomType(reservationRoomStayDTO));
        return arrayOfRoomType;
    }

    private RoomType translateRoomType(ReservationRoomStayDTO reservationRoomStayDTO) {
        RoomType roomType = createRoomType();
        roomType.setRoomTypeCode(reservationRoomStayDTO.getRoomTypeCode());
        roomType.setNumberOfUnits(1);
        return roomType;
    }
}
